package com.m3839.sdk.ym.widget.toast

import android.R
import android.app.Activity
import android.content.Context
import android.graphics.PixelFormat
import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.view.WindowManager.BadTokenException
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import com.m3839.sdk.ym.widget.toast.draggable.BaseDraggable

/**
 * Description:HykbFloatView
 *
 * @author 陈宝阳
 * @create 2019/12/30 14: 48
 */
class YMFloatView private constructor(
    /** 上下文  */
    private val mContext: Context
) {

    /** 根布局  */
    var rootView: View? = null
        private set

    /** 悬浮窗口  */
    val windowManager: WindowManager = mContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager

    /** 窗口参数  */
    val windowParams: WindowManager.LayoutParams?

    /**
     * 当前是否已经显示
     */
    /** 当前是否已经显示  */
    var isShow = false
        private set

    /** 窗口显示时长  */
    private var mDuration = 0

    /** 窗口自动消失回调  */
    private var mListener: OnCancelListener? = null

    /** 自定义拖动处理  */
    private var mDraggable: BaseDraggable? = null

    /**
     * 创建一个局部悬浮窗
     */
    constructor(activity: Activity) : this(activity as Context) {

        // 如果当前 Activity 是全屏模式，那么添加这个标记，否则会导致 WindowManager 在某些机型上移动不到状态栏位置上
        if (activity.window
                .attributes.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN != 0
        ) {
            addWindowFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
        }
    }

    /**
     * 设置动画样式
     */
    fun setAnimStyle(id: Int): YMFloatView {
        windowParams!!.windowAnimations = id
        if (isShow) {
            update()
        }
        return this
    }

    /**
     * 添加一个标志位
     */
    private fun addWindowFlags(flags: Int): YMFloatView {
        windowParams!!.flags = windowParams.flags or flags
        if (isShow) {
            update()
        }
        return this
    }

    /**
     * 是否有这个标志位
     */
    private fun hasWindowFlags(flags: Int): Boolean {
        return windowParams!!.flags and flags != 0
    }

    /**
     * 移除一个标志位
     */
    private fun removeWindowFlags(flags: Int): YMFloatView {
        windowParams!!.flags = windowParams.flags and flags.inv()
        if (isShow) {
            update()
        }
        return this
    }

    /**
     * 根据 ViewId 获取 View
     */
    private fun <V : View?> findViewById(id: Int): V {
        checkNotNull(rootView) { "Please setup view" }
        return rootView!!.findViewById<View>(id) as V
    }

    fun setText(viewId: Int, stringId: Int): YMFloatView {
        return setText(viewId, mContext.resources.getString(stringId))
    }

    fun setText(text: CharSequence?): YMFloatView {
        return setText(R.id.message, text)
    }

    fun setText(id: Int, text: CharSequence?): YMFloatView {
        (findViewById<View>(id) as TextView).text = text
        return this
    }

    /**
     * 设置点击事件
     */
    fun <T: View> setOnClickListener(
        id: Int,
        listener: OnClickListener<out T>
    ): YMFloatView {
        ViewClickWrapper(this, findViewById(id), listener)
        // 当前是否设置了不可触摸，如果是就擦除掉
        if (hasWindowFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)) {
            removeWindowFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
            if (isShow) {
                update()
            }
        }
        return this
    }

    /**
     * 设置 X 轴偏移量
     */
    fun setXOffset(x: Int): YMFloatView {
        windowParams!!.x = x
        if (isShow) {
            update()
        }
        return this
    }

    /**
     * 设置 Y 轴偏移量
     */
    fun setYOffset(y: Int): YMFloatView {
        windowParams!!.y = y
        if (isShow) {
            update()
        }
        return this
    }

    /**
     * 设置重心
     */
    fun setGravity(gravity: Int): YMFloatView {
        windowParams!!.gravity = gravity
        if (isShow) {
            update()
        }
        return this
    }

    /**
     * 设置宽度
     */
    fun setWidth(width: Int): YMFloatView {
        windowParams!!.width = width
        if (isShow) {
            update()
        }
        return this
    }

    /**
     * 设置高度
     */
    fun setHeight(height: Int): YMFloatView {
        windowParams!!.height = height
        if (isShow) {
            update()
        }
        return this
    }

    fun setOnCancelListener(listener: OnCancelListener?): YMFloatView {
        mListener = listener
        return this
    }

    /**
     * 限定显示时长
     */
    fun setDuration(duration: Int): YMFloatView {
        mDuration = duration
        if (isShow) {
            if (mDuration != 0) {
                removeCallbacks()
                postDelayed(DismissRunnable(this, mListener), mDuration.toLong())
            }
        }
        return this
    }

    /**
     * 设置拖动规则
     */
    fun setDraggable(draggable: BaseDraggable?): YMFloatView {
        // 当前是否设置了不可触摸，如果是就擦除掉这个标记
        if (hasWindowFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)) {
            clearWindowFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
        }
        // 当前是否设置了可移动窗口到屏幕之外，如果是就擦除这个标记
        if (hasWindowFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)) {
            clearWindowFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
        }
        mDraggable = draggable
        if (isShow) {
            update()
            mDraggable!!.start(this)
        }
        return this
    }

    /**
     * 移除一个标记位
     */
    private fun clearWindowFlags(flags: Int): YMFloatView {
        windowParams!!.flags = windowParams.flags and flags.inv()
        update()
        return this
    }

    /**
     * 设置布局
     */
    fun setView(id: Int): YMFloatView {
        return setView(LayoutInflater.from(mContext).inflate(id, FrameLayout(mContext), false))
    }

    fun setView(view: View?): YMFloatView {
        rootView = view
        val params = rootView!!.layoutParams
        if (params != null && windowParams!!.width == WindowManager.LayoutParams.WRAP_CONTENT && windowParams.height == WindowManager.LayoutParams.WRAP_CONTENT
        ) {
            // 如果当前 Dialog 的宽高设置了自适应，就以布局中设置的宽高为主
            setWidth(params.width)
            setHeight(params.height)
        }

        // 如果当前没有设置重心，就自动获取布局重心
        if (windowParams!!.gravity == Gravity.NO_GRAVITY) {
            if (params is FrameLayout.LayoutParams) {
                setGravity(params.gravity)
            } else if (params is LinearLayout.LayoutParams) {
                setGravity(params.gravity)
            } else {
                // 默认重心是居中
                setGravity(Gravity.CENTER)
            }
        }
        if (isShow) {
            update()
        }
        return this
    }

    /**
     * 显示
     */
    fun show(): YMFloatView {
        require(!(rootView == null || windowParams == null)) { "WindowParams and view cannot be empty" }

        // 如果当前已经显示取消上一次显示
        if (isShow) {
            cancel()
        }
        try {
            // 如果这个 View 对象被重复添加到 WindowManager 则会抛出异常
            // java.lang.IllegalStateException: View android.widget.TextView{3d2cee7 V.ED..... ......ID 0,0-312,153} has already been added to the window manager.
            windowManager.addView(rootView, windowParams)
            // 当前已经显示
            isShow = true
            // 如果当前限定了显示时长
            if (mDuration != 0) {
                postDelayed(DismissRunnable(this, mListener), mDuration.toLong())
            }

            // 如果设置了拖拽规则
            if (mDraggable != null) {
                mDraggable!!.start(this)
            }
        } catch (e: NullPointerException) {
            e.printStackTrace()
        } catch (e: IllegalStateException) {
            e.printStackTrace()
        } catch (e: BadTokenException) {
            e.printStackTrace()
        }
        return this
    }

    /**
     * 取消
     */
    fun cancel(): YMFloatView {
        if (isShow) {
            try {


                // 如果当前 WindowManager 没有附加这个 View 则会抛出异常
                // java.lang.IllegalArgumentException: View=android.widget.TextView{3d2cee7 V.ED..... ........ 0,0-312,153} not attached to window manager
                windowManager.removeView(rootView)
                // 当前没有显示
                isShow = false
            } catch (e: NullPointerException) {
                e.printStackTrace()
            } catch (e: IllegalArgumentException) {
                e.printStackTrace()
            } catch (e: IllegalStateException) {
                e.printStackTrace()
            }
        }
        return this
    }

    /**
     * 更新
     */
    fun update() {
        // 更新 WindowManger 的显示
        windowManager.updateViewLayout(rootView, windowParams)
    }

    /**
     * 延迟一段时间执行
     */
    private fun postDelayed(r: Runnable?, delayMillis: Long): Boolean {
        var delayMillis = delayMillis
        if (delayMillis < 0) {
            delayMillis = 0
        }
        return postAtTime(r, SystemClock.uptimeMillis() + delayMillis)
    }

    /**
     * 在指定的时间执行
     */
    private fun postAtTime(r: Runnable?, uptimeMillis: Long): Boolean {
        // 发送和这个 WindowManager 相关的消息回调
        return HANDLER.postAtTime(r!!, this, uptimeMillis)
    }

    /**
     * 移除消息回调
     */
    private fun removeCallbacks() {
        HANDLER.removeCallbacksAndMessages(this)
    }

    companion object {
        private val HANDLER = Handler(Looper.getMainLooper())
    }

    init {
        windowParams = WindowManager.LayoutParams()
        // 配置一些默认的参数
        windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT
        windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT
        windowParams.format = PixelFormat.TRANSLUCENT
        windowParams.windowAnimations = R.style.Animation_Toast
        windowParams.flags = (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
        windowParams.packageName = mContext.packageName
    }
}